package com.maixy.redisdemo.redis.template;

import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.GeoOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Component
public class GeoRedisTemplate {

    @Resource(name = "redisTemplate")
    private RedisTemplate<String, String> redisTemplate;

    public boolean exists(String key) {
        Boolean result = redisTemplate.hasKey(key);
        return result == null ? false : result;
    }

    public void del(String key) {
        redisTemplate.delete(key);
    }

    public void delAll(Set<String> list){
        redisTemplate.delete(list);
    }

    public void delAll(List<String> list){
        redisTemplate.delete(list);
    }

    public void expire(String key, long timeout) {
        redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
    }

    public void expire(String key, long timeout, TimeUnit timeUnit) {
        redisTemplate.expire(key, timeout, timeUnit);
    }

    public long ttl(String key) {
        return redisTemplate.getExpire(key);
    }

    /**
     * GEOADD 添加一个或多个地理位置元素到一个key中
     格式：GEOADD key longitude latitude member [longitude latitude member ...]
     * @param key key
     * @param location location
     */
    public void geoadd(String key, RedisGeoCommands.GeoLocation<String> location) {
        redisTemplate.opsForGeo().geoAdd(key, location);
    }

    public void geoadd(String key, double x, double y, String member) {
        GeoOperations<String, String> geoOps = redisTemplate.opsForGeo();
        geoOps.geoAdd(key, new Point(x, y) , member);
    }

    /**
     * GEOPOS 返回一个或多个位置的经纬度信息，由于采用了geohash算法，返回的经纬度和添加时的数据可能会有细小误差
     格式： GEOPOS key member [member ...]
     * @param key key
     * @param member member
     * @return point
     */
    public Point geopos(String key, String member) {
        List<Point> points = redisTemplate.opsForGeo().geoPos(key, member);
        if(null == points || points.isEmpty()) {
            return null;
        }else {
            return points.get(0);
        }
    }

    /**
     * 删除元素
     * @param key
     * @param member
     */
    public void georemove(String key, String... member) {
        if(member != null) {
            redisTemplate.opsForGeo().geoRemove(key, member);
        }
    }

    /**
     * GEORADIUS  以给定位置为中心，半径不超过给定半径的附近所有位置
     格式 GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] ASC|DESC
     */
    public GeoResults<RedisGeoCommands.GeoLocation<String>> georadius(String key, double x, double y, double radius, RedisGeoCommands.GeoRadiusCommandArgs args) {
        Circle circle = new Circle(new Point(x, y), radius);
        if(args == null) {
            return redisTemplate.opsForGeo().geoRadius(key, circle);
        } else {
            return redisTemplate.opsForGeo().geoRadius(key, circle, args);
        }
    }

    public GeoResults<RedisGeoCommands.GeoLocation<String>> findListWithRadius(String key, double x, double y, double distance, String direction, long limit) {
        GeoOperations<String, String> geoOps = redisTemplate.opsForGeo();

        //设置geo查询参数
        RedisGeoCommands.GeoRadiusCommandArgs geoRadiusArgs = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
        geoRadiusArgs = geoRadiusArgs.includeCoordinates().includeDistance(); //查询返回结果包括距离和坐标
        if ("asc".equals(direction)) { //按查询出的坐标距离中心坐标的距离进行排序
            geoRadiusArgs.sortAscending();
        } else if ("desc".equals(direction)) {
            geoRadiusArgs.sortDescending();
        }
        geoRadiusArgs.limit(limit); //限制查询数量
        GeoResults<RedisGeoCommands.GeoLocation<String>> radiusGeo = geoOps.geoRadius(key, new Circle(new Point(x, y), new Distance(distance, RedisGeoCommands.DistanceUnit.METERS)), geoRadiusArgs);
        return radiusGeo;
    }
}
